# Create your views here.
from django.template import RequestContext, Context, loader
from django.http import HttpResponse
from django.shortcuts import render_to_response, render
from django.contrib.auth import authenticate, login, logout
from django.core.context_processors import csrf
from jarvis.models import *
from django.views.decorators.csrf import csrf_exempt
from django import forms
import jarvis.cm19aDriver as cm19a

firecracker = None
firecracker_status = "Not connected"
Level = 0

def printName(fn):
    def wrapper(*args, **kwargs):
        global Level
        print '\t'*Level, "Entering ",  fn.__name__
        Level += 1
        result = fn(*args, **kwargs)
        Level -= 1
        print '\t'*Level, "Leaving ", fn.__name__
        return result
    return wrapper

def printName(fn):
    return fn

#Form classes
class AddAreaForm(forms.Form):
    Area_Name = forms.CharField(max_length=40)
    options = [(chr(65 + x), chr(65 + x)) for x in range(15)]
    Area_Code = forms.ChoiceField(choices=options)

class AddDeviceForm(forms.Form):
    Area_Name = forms.ChoiceField()
    Device_Name = forms.CharField(max_length=40)
    options = [(x+1,x+1) for x in range(15)]
    Device_Code = forms.ChoiceField(choices=options)

class AddGroupForm(forms.Form):
    Group_Name = forms.CharField(max_length=40)

class RegisterForm(forms.Form):
    user_name = forms.CharField(max_length=100)
    email_address = forms.CharField(max_length=50 )
    password = forms.CharField(widget=forms.PasswordInput)

class LoginForm(forms.Form):
    user_name = forms.CharField(max_length=100)
    password = forms.CharField(widget=forms.PasswordInput)

#Get helpers

#User helpers
@printName
def getUserName(request):
    if 'username' in request.session:
        user = request.session['username']
        error = False
    else:
        user = ""
        error = True
    return error, user

#Lookup IDs from common names
@printName
def getUserId(user):
    user = User.objects.filter(username=user)
    if len(user) == 0:
        return None
    else:
        return user[0].id

@printName    
def getAreaId(area_name):
    area = Area.objects.filter(area_name=area_name)
    if len(area) > 0:
        return area[0].id
    else:
        return None

@printName
def getGroupId(group_name):
    group = Group.objects.filter(group_name=group_name)
    if len(group) > 0:
        return group[0].id
    else:
        return None

#Get lists to populate sheets
@printName
def getAreaListAndCounts(user):
    if user == "Local":
        area_list = Area.objects.all()
    else:
        area_list = Area.objects.filter(user=getUserId(user))
    counts = []
    for area in area_list:
        counts.append(len(Device.objects.filter(area=area)))
    return area_list, counts

@printName
def getGroupListAndCounts(user):
    if user == "Local":
        group_list = Group.objects.all()
    else:
        group_list = Group.objects.filter(user=getUserId(user))
    counts = []
    for group in group_list:
        counts.append(len(group.device_list.split()))
    return group_list, counts

@printName
def getAreaAndDeviceDictionary(user):
    if user == "Jarvis":
        area_list = Area.objects.all()
    else:
        area_list = Area.objects.filter(user=getUserId(user))
    areas = []
    for area in area_list:
        areas.append([area.area_name,Device.objects.filter(area=area)])     
    return areas
    
@printName
def getAreaList(user, area=None):
    filter = {}
    if area:
        filter['area_name'] = area
    if not user == "Jarvis":
        filter['user'] = getUserId(user)
    return Area.objects.filter(**filter)

@printName
def getDeviceListByGroup(user, group_name):
    try:
        user_id = getUserId(user)
        group = Group.objects.filter(user_id=user_id, group_name=group_name)
        devices = Device.objects.filter(user_id=user_id)
        device_list = []
        for device_name in group.device_list:
            try:
                device = devices.filter(device_name=device_name)
                device_list.append(device)
            except:
                pass
        return device_list
    except:
        return None

@printName
def getDeviceListByArea(user, area_name):
    try:
        user_id = getUserId(user)
        area_id = Area.objects.filter(user_id=user_id, area_name=area_name)[0].id
        return Device.objects.filter(area_id=area_id)
    except:
        return None

#####################
#Determine if on local Network
@printName
def onLocalNetwork(request):
    ip = get_client_ip(request)
    if ip.startswith('192.168'):
        return True
    return False


@printName
def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[-1].strip()
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip


###########################
#Wrapper for rendering with context

@printName
def render_wrapper(request, template, dict):
    error, user = getUserName(request)
    if not error:
        dict['username'] = user
    return render_to_response(template, dict, context_instance=RequestContext(request))

################################################



#Views
########################
@printName
def index(request):
    error = False
    error, user = getUserName(request)
    area_list, counts = getAreaListAndCounts(user)
    return render_wrapper(request, 'jarvis/index.html', {'area_list': zip(area_list, counts), 'username':user, 'error':error})

@printName
def area_list(request):
    error, user = getUserName(request)
    area_list, counts = getAreaListAndCounts(user)
    return render_wrapper(request, 'jarvis/areas.html', {'username': user,  'area_list':zip(area_list, counts)})

@printName
def device_list(request):
    error, user = getUserName(request)
    entries = getAreaAndDeviceDictionary(user)
    return render_wrapper(request, 'jarvis/devices.html', {'username': user, 'entries':entries})

@printName
def group_list(request):
    error, user = getUserName(request)
    group_list, counts = getGroupListAndCounts(user)
    return render_wrapper(request, 'jarvis/groups.html', {'username':user, 'group_list':zip(group_list, counts)})

@printName
def area_view(request, area_name):
    error, user = getUserName(request)
    device_list = getDeviceListByArea(user, area_name)
    return render_wrapper(request, 'jarvis/area.html', {'error':error, 'username':user, 'device_list': device_list, "area_name":area_name})

@printName
def group_view(request, group_name):
    error, user = getUserName(request)
    device_list = getDeviceListByGroup(user, group_name)
    return render_wrapper(request, 'jarvis/area.html', {'error':error, 'username':user, 'device_list': device_list, "area_name":group_name})

@printName
def device_view(request, area_name, device_name):
    user = getUserName(request)
    area = getDeviceListByArea(user, area_name).filter(device_name=device_name)
    return render_wrapper(request, 'jarvis/device.html', {'error':error, 'username':user, 'device':device, 'area':area})

@printName
def option_view(request):

    return render_wrapper(request, 'jarvis/options.html', {'firecracker_status':firecracker_status})

@printName
def add_area_form(request):
    error = False
    error, user = getUserName(request)
    if request.method == 'POST':
        form = AddAreaForm(request.POST)
        if form.is_valid():
            error,message = add_area(form, user)
            return area_list(request)
    else: 
        form = AddAreaForm()
    return render_wrapper(request, 'jarvis/addareaform.html', {'form':form,})

@printName
def add_device_form(request):
    error = False
    error, user = getUserName(request)
    if request.method == 'POST':
        form = AddDeviceForm(request.POST)
        if form.is_valid():
            error, message = add_device(form, user)
            #TODO Error check
            return area_view(request, form.data['Area_Name'])
    else:
        form = AddDeviceForm()
        if not error:
            areas = getAreaList(user)
            options =[(x.area_name, x.area_name) for x in areas]
            form.base_fields['Area_Name'] = forms.ChoiceField(options)
        form = AddDeviceForm()
    return render_wrapper(request, 'jarvis/adddeviceform.html', {'form':form, 'error':error})

@printName
def add_group_form(request):
    error = False
    error, user = getUserName(request)
    if request.method == 'POST':
        form = AddGroupForm(request.POST)
        if form.is_valid():
            error, message = add_group(form, user)
            return group_list(request)
    else: 
        form = AddGroupForm()
    return render_wrapper(request, 'jarvis/addgroupform.html', {'form':form,})

@printName
def login_form(request):
    c = {}
    c.update(csrf(request))
    error =False
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            return check_login(request,form)
    else:
        form = LoginForm()
    return render_wrapper(request, 'jarvis/loginform.html', {'form':form, 'error':error, })


# Database manipulators
###############################

@printName
def add_area(form, user):
    area = form.data['Area_Name']
    code = form.data['Area_Code']
    uid = getUserId(user)
    numareas = len(getAreaList(user, area))
    if numareas > 0:
        error =True
        message = "Area already exists"
        return error, message
    x = Area(user_id=uid, area_name=area, house_code=code)
    x.save()
    return 0, ""

@printName
def add_device(form, user):
    area = form.data['Area_Name']
    device = form.data['Device_Name']
    uid = getUserId(user)
    areaId =getAreaId(area)
    if not areaId:
        return 1, "Error does not exist in data base, you fucked up"
    numdevices = len(Device.objects.filter(user=uid).filter(area=areaId).filter(device_name=device))
    if numdevices > 0:
        error =True
        message = "Device %s that name already exists in %s"%(area, device)
        return error, message

    x = Device(user_id=uid, device_name=device, device_code=form.data['Device_Code'], area=Area.objects.get(pk=areaId))
    x.save()
    #No error
    return 0, ""
    
@printName
def add_group(form, user):
    group = form.data['Group_Name']
    uid = getUserId(user)
    numgroups = len(Group.objects.filter(user=uid).filter(group_name=group))
    if numgroups > 0:
        error =True
        message = "Group %s already exists"%group
        return error, message
    x = Group(user_id=uid, group_name=group, device_list="")
    x.save()
    return 0, ""


# Login 
##############################
@printName
def check_login(request,form):
    global firecracker
    firecracker = cm19a.configure()
    area_list = []
    counts = []                    
    error = False
    message = []
    username = form.data['user_name']
    password = form.data['password']
    user = authenticate(username=username, password=password)
    if user is not None:
        if user.is_active:
            login(request, user)
            request.session['username'] = username
            if onLocalNetwork(request):
                request.session['onLocalNetwork'] = False
            else:
                request.session['onLocalNetwork'] = False

            area_list, counts = getAreaListAndCounts(user)
        else:
            error = True
            message.append( "Your account is not active, please contact the site admin.")
    else:
        error = True
        username = ""
        message.append("Your username and/or password were incorrect.")


    if not firecracker.initialised:
        error = True
        message.append("Could not initialize USB controller")
        firecracker_status = "Not Connected"
    else:
        firecracker_status = "Connected"
    return render_wrapper(request, 'jarvis/index.html', {'username': username, 'error':error, 'message':message})

@printName
def logout_user(request):
    global firecracker
    try:
        cm19a.tidyUp(firecracker)
    except:
        pass
    logout(request)
    #request.session['username'] = None
    firecracker_status = "Not connected"
    return render_wrapper(request, 'jarvis/index.html', {'error':True})

#############################################
#AJAX Destinations, control state of system
##############################################

##################################################
#AJAX Destination, set the device at destination
##################################################
def set_light(request, area_name, device_name, state):

    #Firecracker status
    global firecracker
    if not firecracker:
        firecracker = cm19a.configure()
    if not firecracker:
        firecracker_status = "Not connected"
        return HttpResponse("Could not initialize USB controller")
    else:
        firecracker_status =  "Connected"


    #Get Device
    error, user = getUserName(request)
    area = Area.objects.get(area_name=area_name, user=getUserId(user))
    device = Device.objects.get(area_id=area.id, device_name=device_name)
    if state == "on":
        state = True
    elif state == "off":
        state = False
    else:
        return HttpResponse("Failure, unknown value")

    if firecracker.send(str(area.house_code), str(device.device_code), state):
        device.enabled = state
        device.save()
    else:
        try:
            cm19a.tidyUp(firecracker)
            firecracker = None
        except:
            pass
    return HttpResponse("It worked: %s %s %s"%(area_name, device_name, area.house_code), mimetype="text/plain")

####################################################
#AJAX Destination, delete the device at destination#
####################################################
def remove_device(request, area_name, device_name):
    error, user = getUserName(request)
    area = Area.objects.get(area_name=area_name, user=getUserId(user))
    device = Device.objects.get(area_id=area.id, device_name=device_name)
    device.delete()
    return HttpResponse("Removed %s devices"%device, mimetype="text/plain")

##################################################
# AJAX Destination, delete the area and all devices in that area
##################################################
def remove_area(request, area_name):
    error,user = getUserName(request)
    if onLocalNetwork(request):
        area = Area.objects.get(area_name=area_name)
    else:
        area = Area.objects.get(area_name=area_name, user=getUserId(user))
    device_list = Device.objects.filter(area_id=area.id)

    for device in device_list:
        device.delete()
    area.delete()
    return HttpResponse("Removed 1 area and %d devices"%len(device_list), mimetype="text/plain")

